package com.xy.smartsms.util;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.Environment;
import android.text.TextUtils;
import android.util.LruCache;

import com.jakewharton.disklrucache.DiskLruCache;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import cn.com.xy.sms.sdk.net.NetUtil;

/**
 * Created by admin on 2017/4/11.
 */
public class BitmapLoader {
    private static final String TAG = BitmapLoader.class.getSimpleName();

    private static final int IMAGE_WIDTH = 200;
    private static final int IMAGE_HIGHT = 200;

    // maximum size for disk cache (bytes)
    private static final int MAX_DISK_CACHE = 10 * 1024 * 1024;
    // divide of maximum size for memory cache from the maximum application memory
    private static final int MAX_MEMORY_CACHE_DIVIEND = 20;
    private static final String CACHE_FILENAME = "thumb";
    private static final int LOAD_INTERVAL = 60 * 1000;

    private Context mContext;
    private static ExecutorService sLoadPool = null;
    private BitmapCallable mBitmapCallable = null;
    private LruCache<String, BitmapDrawable> mMemoryCache;
    private DiskLruCache mDiskCache;
    private static HashMap<String, Long> sLastLoadTime = new HashMap<String, Long>();
    private static BitmapLoader sBitmapLoader = null;

    public static BitmapLoader getInstance(Context context) {
        synchronized (BitmapLoader.class) {
            if (sBitmapLoader == null) {
                sBitmapLoader = new BitmapLoader(context);
            }
        }

        return sBitmapLoader;
    }

    private BitmapLoader(Context context) {
        mContext = context;
        mBitmapCallable = new BitmapCallable(mContext);
        initCache();
    }

    private synchronized ExecutorService getLoadPool() {
        if (sLoadPool == null) {
            sLoadPool = Executors.newFixedThreadPool(1);
        }

        return sLoadPool;
    }

    /**
     * 图片加载回调接口
     */
    public interface ImageCallback {
        /**
         * 加载完成
         *
         * @param url      标识当前图片
         * @param drawable 实际图片
         */
        public void imageLoad(final String url, final BitmapDrawable drawable);
    }

    private void initCache() {
        // maximum memory that application can use
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / MAX_MEMORY_CACHE_DIVIEND;

        mMemoryCache = new LruCache<String, BitmapDrawable>(cacheSize) {
            @Override
            protected int sizeOf(String key, BitmapDrawable bitmapDrawable) {
                return bitmapDrawable.getBitmap().getByteCount();
            }
        };

        try {
            File cacheDir = getDiskCacheDir(CACHE_FILENAME);
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }

            mDiskCache = DiskLruCache.open(cacheDir, getAppVersion(), 1, MAX_DISK_CACHE);
        } catch (Throwable t) {
            Log.e(TAG, "initCache", t);
        }
    }

    private File getDiskCacheDir(String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
            cachePath = mContext.getExternalCacheDir().getPath();
        } else {
            cachePath = mContext.getCacheDir().getPath();
        }

        return new File(cachePath + File.separator + uniqueName);
    }

    private int getAppVersion() {
        try {
            PackageInfo info = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
            return info.versionCode;
        } catch (Throwable t) {
            Log.i(TAG, "getAppVersion: ");
        }

        return 1;
    }

    /**
     * 设置文件缓存大小，默认为10M
     *
     * @param cacheSize 缓冲区大小(Byte)
     */
    public void setCacheSize(int cacheSize) {
        mDiskCache.setMaxSize(cacheSize);
    }

    private BitmapDrawable loadBitmapApi(String key, String url) {
        if (!NetUtil.checkAccessNetWork()) {
            return null;
        }
        BitmapDrawable bitmapDrawable = null;
        // introduce interval to reduce redundant network access
        Long time = sLastLoadTime.get(key);
        if (time != null && (System.currentTimeMillis() < (time + LOAD_INTERVAL))) {
            return null;
        }

        try {
            final DiskLruCache.Editor editor = mDiskCache.edit(key);
            if (editor != null) {
                OutputStream outputStream = editor.newOutputStream(0);
                mBitmapCallable.setParam(url, outputStream);
                Boolean result = mBitmapCallable.executeWithTimeout(6000);
                if (result) {
                    editor.commit();
                } else {
                    editor.abort();
                }
                DiskLruCache.Snapshot snapShot = null;
                try {
                    snapShot = mDiskCache.get(key);
                    bitmapDrawable = decodeAndUpdateCache(key, snapShot);
                } finally {
                    if (snapShot != null) {
                        snapShot.close();
                    }
                }
            }
        } catch (Throwable t) {
            Log.e(TAG, "loadBitmapAsync", t);
        }

        return bitmapDrawable;
    }

    public void removeLogo(String key) {
        try {
            mMemoryCache.remove(key);
            mDiskCache.remove(key);
        } catch (Throwable t) {
            Log.e(TAG, "removeLogo", t);
        }
    }

    /**
     * 获取图片
     *
     * @param key           唯一标识
     * @param url           待加载的路径地址
     * @param imageCallback 异步加载的回调，为null说明只同步获取
     * @return 如果为null，则表示需异步加载，需要等待回调{@link ImageCallback}
     */
    public BitmapDrawable loadBitmap(final String key, final String url, final ImageCallback imageCallback) {
        if (TextUtils.isEmpty(key) || TextUtils.isEmpty(url)) {
            return null;
        }

        BitmapDrawable drawable = getBitmapFromMemoryCache(key);
        if (drawable != null && !drawable.getBitmap().isRecycled()) {
            return drawable;
        }

        if (imageCallback == null) {
            return null;
        }

        getLoadPool().execute(new Runnable() {
            @Override
            public void run() {
                BitmapDrawable result = getBitmapFromMemoryCache(key);
                if (result == null) {
                    DiskLruCache.Snapshot snapShot = null;
                    try {
                        snapShot = mDiskCache.get(key);
                        if (snapShot == null) {
                            Log.v(TAG, "run: loadBitmapApi");
                            result = loadBitmapApi(key, url);
                        } else {
                            Log.v(TAG, "run: decodeAndUpdateCache");
                            result = decodeAndUpdateCache(key, snapShot);
                        }
                    } catch (Throwable t) {
                        Log.e(TAG, "loadBitmap", t);
                    } finally {
                        if (snapShot != null) {
                            snapShot.close();
                        }
                    }
                }

                if (imageCallback != null) {
                    imageCallback.imageLoad(url, result);
                }
            }
        });

        return null;
    }

    private BitmapDrawable getRoundedCornerBitmap(BitmapDrawable bitmapDrawable) {
        if (bitmapDrawable == null)
            return null;
        Bitmap bitmap = bitmapDrawable.getBitmap();
        if (bitmap == null)
            return null;
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        final int color = 0xFF000000;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, width, height);
        final RectF rectF = new RectF(rect);
        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        float roundPx = width / 2;
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);
        if (bitmap != null && !bitmap.isRecycled()) {
            bitmap.recycle();
            bitmap = null;
        }

        int StrokeWidth = 2;
        Bitmap output_1 = Bitmap.createBitmap(width + 2 * StrokeWidth, height
                + 2 * StrokeWidth, Bitmap.Config.ARGB_8888);
        Canvas canvas_1 = new Canvas(output_1);
        Paint paint_1 = new Paint();
        paint_1.setAntiAlias(true);
        paint_1.setColor(Color.parseColor("#CECECE"));
        paint_1.setStyle(Paint.Style.STROKE);
        paint_1.setStrokeWidth(StrokeWidth);
        canvas_1.drawCircle(width / 2 + StrokeWidth, height / 2 + StrokeWidth,
                width / 2, paint_1);
        canvas_1.drawBitmap(output, StrokeWidth, StrokeWidth, paint_1);
        if (output != null && !output.isRecycled()) {
            output.recycle();
            output = null;
        }

        return new BitmapDrawable(mContext.getResources(), output_1);
    }

    private BitmapDrawable decodeAndUpdateCache(String key, DiskLruCache.Snapshot snapShot) {
        BitmapDrawable drawable = decodeBitmap(snapShot);
        if (drawable != null) {
            drawable = getRoundedCornerBitmap(drawable);
            addBitmapToMemoryCache(key, drawable);
        } else {
            sLastLoadTime.put(key, System.currentTimeMillis());
        }

        return drawable;
    }

    private BitmapDrawable decodeBitmap(DiskLruCache.Snapshot snapShot) {
        try {
            if (snapShot != null) {
                FileInputStream fileInputStream = (FileInputStream) snapShot.getInputStream(0);
                FileDescriptor fileDescriptor = fileInputStream.getFD();
                final BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
                int w = options.outWidth;
                // int h = options.outHeight;
                options.inDensity = w;
                options.inTargetDensity = IMAGE_WIDTH;
                options.inJustDecodeBounds = false;

                Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
                bitmap.setDensity(w);
                BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), bitmap);
                return drawable;
            }
        } catch (Throwable t) {
            Log.e(TAG, "decodeBitmap", t);
        }

        return null;
    }

    private void addBitmapToMemoryCache(String key, BitmapDrawable drawable) {
        if (getBitmapFromMemoryCache(key) == null) {
            mMemoryCache.put(key, drawable);
        }
    }

    private BitmapDrawable getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }
}
